﻿using CKS2320.Common;
using CKS2320.Common.Model;
using CKS2320.Common.Utils;
using S7.Net;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.Eventing.Reader;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataType = S7.Net.DataType;

namespace CKS2320.Controller
{
    /// <summary>
    /// 西门子PLC控制器
    /// </summary>
    public class PLCController
    {
        public delegate void AddLogHandler(string content, string source = "PLC");
        public static AddLogHandler AddLogEventHandler;
        private C_LogWriter log;
        public PLCController() {
            log = new C_LogWriter(LogType.InfoLog);
        }
        public Plc? plc = null;

        /// <summary>
        /// 打开连接
        /// </summary>
        /// <param name="cputype">PLC类型</param>
        /// <param name="ip">IP地址</param>
        /// <param name="rack">机架号</param>
        /// <param name="slot">槽号</param>
        /// <returns></returns>
        public ResultReturn Open(CpuType cputype, string ip, short rack, short slot)
        {
            try
            {
                plc = new Plc(cputype, ip, rack, slot);
                plc.Open();
                if (plc.IsConnected)
                {
                    return MessageInfoUtils.Success();
                }
            }
            catch (Exception ex)
            {
                return MessageInfoUtils.Fail(ex.Message);
            }
            return MessageInfoUtils.Fail();
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            bool result = false;
            if (plc != null)
            {
                plc.Close();
                if (!plc.IsConnected)
                    result = true;
            }
            else
            {
                result = true;
            }
            return result;
        }

        public bool _RecState = false;

        /// <summary>
        /// 检查连接是否存在
        /// </summary>
        /// <returns></returns>
        public bool IsConnected()
        {

            try
            {
                return plc.IsConnected;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public bool CheckConnect()
        {
            try
            {
                bool result = false;
                if (plc != null)
                {
                    if (plc.IsConnected)
                        result = true;
                    else//重连
                    {
                        plc.Open();
                        result = plc.IsConnected;

                    }
                }
                return result;
            }
            catch (Exception)
            {
                return false;
            }
        }
      



        /// <summary>
        /// 获取PLC型号
        /// </summary>
        /// <param name="cputype"></param>
        /// <returns></returns>
        public CpuType transferCputype(string cputype)
        {
            switch (cputype.ToLower())
            {
                case "s7-200":
                    return CpuType.S7200;

                case "logo0ba8":
                    return CpuType.Logo0BA8;

                case "s7-200smart":
                    return CpuType.S7200Smart;

                case "s7-300":
                    return CpuType.S7300;

                case "s7-400":
                    return CpuType.S7400;

                case "s7-1200":
                    return CpuType.S71200;

                case "s7-1500":
                    return CpuType.S71500;

                default:
                    break;
            }
            return CpuType.S7200;
        }

        /// <summary>
        /// 写入Bool值到PLC
        /// </summary>
        /// <param name="db">db块</param>
        /// <param name="startByteAdr">偏移值</param>
        /// <param name="value">写入的值</param>
        public void WriteBool(int db, string startByteAdr, bool value)
        {
            string variable = string.Format("DB{0}.{1}{2}", db, "DBX", startByteAdr);
            if (IsConnected())
            {
                plc?.Write(variable, value);
            }
        }

        /// <summary>
        /// 写入int值到PLC
        /// </summary>
        /// <param name="db">db块</param>
        ///// <param name="startByteAdr">偏移值</param>
        ///// <param name="value">写入的值</param>
        public void WriteInt(int db, string startByteAdr, int value)
        {
            // 将整数值转换为字节数组
            byte[] bytes = new byte[2]; // 一个short类型占用2个字节
            bytes[0] = (byte)((value >> 8) & 0xFF); // 高位字节
            bytes[1] = (byte)(value & 0xFF); // 低位字节
            int startByte = int.Parse(startByteAdr); // 起始字节
            plc.WriteBytes(DataType.DataBlock, db, startByte, bytes);
            var val = (UInt16)(plc.Read("DB" + db + ".DBW" + startByteAdr));
            int retryCount = 0;
            while (true)
            {
                
                retryCount++;
                if (val != value)
                {
                    if (retryCount > 10)
                    {
                        //
                        AddLogEventHandler?.Invoke($"写入地址未成功DB{db}.{startByteAdr}");
                        break;
                    }
                    plc.WriteBytes(DataType.DataBlock, db, startByte, bytes);
                    val = (UInt16)(plc.Read("DB" + db + ".DBW" + startByteAdr));
                    Thread.Sleep(20);
                }
                else
                {
                    return;
                }
            }
        }

        //public void WriteInt(int db, string startByteAdr, bool value)
        //{
        //    string variable = string.Format("DB{0}.{1}{2}", db, "DBW", startByteAdr);
        //    if (IsConnected())
        //    {
        //        plc?.Write(variable, value);
        //    }
        //}
        public void WriteAllType(int db, string startByteAdr, VarType vartType, object value)
        {
            string dbType = string.Empty;
            switch (vartType)
            {
                case VarType.Bit:
                    dbType = "DBX";
                    break;

                case VarType.Byte:
                    break;

                case VarType.Word:
                    break;

                case VarType.DWord:
                    break;

                case VarType.Int:
                    dbType = "DBW";
                    break;

                case VarType.DInt:
                    break;

                case VarType.Real:
                case VarType.LReal:
                    dbType = "DBD";
                    break;

                case VarType.String:
                    break;

                case VarType.S7String:
                    break;

                case VarType.S7WString:
                    break;

                case VarType.Timer:
                    break;

                case VarType.Counter:
                    break;

                case VarType.DateTime:
                    break;

                case VarType.DateTimeLong:
                    break;

                default:
                    break;
            }
            string variable = string.Format("DB{0}.{1}{2}", db, dbType, startByteAdr);
            if (IsConnected())
            {
                plc?.Write(variable, value);
            }
        }

        /// <summary>
        /// 读取real值
        /// </summary>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        /// <returns></returns>
        public float? ReadReal(int db, string startByteAdr)
        {
            if (IsConnected())
            {
                string variable = string.Format("DB{0}.{1}{2}", db, "DBD", startByteAdr);
                object? variableResult = plc?.Read(variable);
                if (variableResult != null)
                {
                    return ((uint)variableResult).ConvertToFloat();
                }
            }
            return null;
        }

        /// <summary>
        /// 读取bool值
        /// </summary>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        public bool? ReadBool(int db, string startByteAdr)
        {
            string variable = string.Format("DB{0}.{1}{2}", db, "DBX", startByteAdr);
            return (bool?)plc?.Read(variable);
        }

        /// <summary>
        /// 读取string值
        /// </summary>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        public string? ReadString(int db, int startByteAdr, int count)
        {
            byte[] dataS = plc.ReadBytes(S7.Net.DataType.DataBlock, db, startByteAdr, count);
            int stringLen = dataS[1];
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
            string gbkString = Encoding.GetEncoding("GBK").GetString(dataS, 2, stringLen);
            return gbkString;
        }

        /// <summary>
        /// 读取string值
        /// </summary>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        public byte[] ReadBytes(int db, int startAddress, int numBytes)
        {
            byte[] dataS = plc.ReadBytes(S7.Net.DataType.DataBlock, db, startAddress, numBytes);
            return dataS;
        }

        /// <summary>
        /// 读取bit类型数据
        /// </summary>
        /// <param name="DataType"></param>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        /// <param name="varCount"></param>
        /// <param name="bitAdr"></param>
        /// <returns></returns>
        public List<bool> ReadBitList(int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            List<bool> boolResult = new List<bool>();
            if (IsConnected())
            {
                var bitReuslt = (System.Collections.BitArray?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.Bit, varCount, bitAdr);
                if (bitReuslt != null)
                {
                    for (int i = 0; i < bitReuslt.Length; i++)
                    {
                        boolResult.Add(bitReuslt.Get(i));
                    }
                }
            }
            return boolResult;
        }

        /// <summary>
        /// 读取string类型数据
        /// </summary>
        /// <param name="DataType"></param>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        /// <param name="varCount"></param>
        /// <param name="bitAdr"></param>
        /// <returns></returns>
        public string? ReadString(int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            if (IsConnected())
            {
                string? result = (string?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.String, varCount, bitAdr);
                return result;
            }
            return null;
        }

        public float? ReadReal(int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            if (IsConnected())
            {
                float? result = (float?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.Real, varCount, bitAdr);
                if (result != null)
                {
                    return result;
                }
            }
            return null;
        }

        public short[] ReadInt(int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            if (IsConnected())
            {
                short[] result = (short[])plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.Int, varCount, bitAdr);
                return result;
            }
            return new short[] { 0 };
        }

        public short ReadIntOne(int db, int startByteAdr, byte bitAdr = 0)
        {
            if (IsConnected())
            {
                short result = (short)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.Int, 1, bitAdr);
                return result;
            }
            return 0;
        }

        /// <summary>
        /// 读取Real类型数据
        /// </summary>
        /// <param name="DataType"></param>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        /// <param name="varCount"></param>
        /// <param name="bitAdr"></param>
        /// <returns></returns>
        public List<float?> ReadRealArray(int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            List<float?> floatResult = new List<float?>();
            if (IsConnected())
            {
                float[]? result = (float[]?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.Real, varCount, bitAdr);
                if (result != null)
                {
                    foreach (var item in result)
                    {
                        floatResult.Add(item);
                    }
                }
            }
            return floatResult;
        }

        public List<short?> ReadLRealArray(int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            List<short?> floatResult = new List<short?>();
            if (IsConnected())
            {
                short[]? result = (short[]?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.LReal, varCount, bitAdr); ;
                if (result != null)
                {
                    foreach (var item in result)
                    {
                        floatResult.Add(item);
                    }
                }
            }
            return floatResult;
        }

        //public DetectionDetail ReadRealAndLRealOne(int db, int startByteAdr, string controllerType, byte bitAdr = 0)
        //{
        //    DetectionDetail reesult = new();
        //    if (IsConnected())
        //    {
        //        double? LRealResult = (double?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.LReal, 1, bitAdr);
        //        if (LRealResult != null)
        //        {
        //            reesult.DetectedData = LRealResult;
        //            float? RealResult = (float?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr + 8, VarType.Real, 1, bitAdr);
        //            if (RealResult != null)
        //            {
        //                //reesult.Position = RealResult;
        //            }
        //        }
        //        //reesult.ControllerType = controllerType;
        //    }
        //    return reesult;
        //}
        //public DetectionDetail ReadLRealOne(int db, int startByteAdr, string controllerType, byte bitAdr = 0)
        //{
        //    DetectionDetail reesult = new();
        //    if (IsConnected())
        //    {
        //        double? LRealResult = (double?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.LReal, 1, bitAdr);
        //        if (LRealResult != null)
        //        {
        //            reesult.DetectedData = LRealResult;
        //        }
        //        //reesult.ControllerType = controllerType;
        //        //reesult.Position = 0;
        //    }
        //    return reesult;
        //}
        /// <summary>
        /// 获取PLC Real类型数据
        /// </summary>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        /// <param name="bitAdr"></param>
        /// <returns></returns>
        public float? ReadRealOne(int db, int startByteAdr, byte bitAdr = 0)
        {
            if (IsConnected())
            {
                float? RealResult = (float?)plc?.Read(S7.Net.DataType.DataBlock, db, startByteAdr, VarType.Real, 1, bitAdr);
                if (RealResult != null)
                {
                    RealResult = (float)Math.Round(RealResult.Value, 3);
                    return RealResult;
                }
            }
            return null;
        }

        /// <summary>
        /// 单个读取示例
        /// </summary>
        /// <param name="cboReadVarType"></param>
        /// <param name="DataType"></param>
        /// <param name="db"></param>
        /// <param name="startByteAdr"></param>
        /// <param name="varCount"></param>
        /// <param name="bitAdr"></param>
        /// <returns></returns>
        public object? ReadController(VarType cboReadVarType, S7.Net.DataType DataType, int db, int startByteAdr, int varCount, byte bitAdr = 0)
        {
            object? result = null;
            if (plc != null)
            {
                switch (cboReadVarType)
                {
                    case VarType.Bit:
                        result = (System.Collections.BitArray?)plc.Read(DataType, db, startByteAdr, VarType.Bit, varCount, bitAdr);
                        break;

                    case VarType.Byte:
                        result = (byte[]?)plc.Read(DataType, db, startByteAdr, VarType.Byte, varCount, bitAdr);
                        break;

                    case VarType.Word:
                        result = (ushort[]?)plc.Read(DataType, db, startByteAdr, VarType.Word, varCount, bitAdr);
                        break;

                    case VarType.DWord:
                        result = (uint[]?)plc.Read(DataType, db, startByteAdr, VarType.DWord, varCount, bitAdr);
                        break;

                    case VarType.Int:
                        result = (short[]?)plc.Read(DataType, db, startByteAdr, VarType.Int, varCount, bitAdr);
                        break;

                    case VarType.DInt:
                        result = (int[]?)plc.Read(DataType, db, startByteAdr, VarType.DInt, varCount, bitAdr);
                        break;

                    case VarType.Real:
                        result = (float[]?)plc.Read(DataType, db, startByteAdr, VarType.Real, varCount, bitAdr);
                        break;

                    case VarType.LReal:
                        result = (double[]?)plc.Read(DataType, db, startByteAdr, VarType.LReal, varCount, bitAdr);
                        break;

                    case VarType.String:
                        result = (string?)plc.Read(DataType, db, startByteAdr, VarType.String, varCount, bitAdr);
                        break;

                    case VarType.S7String:
                        result = (string[]?)plc.Read(DataType, db, startByteAdr, VarType.S7String, varCount, bitAdr);
                        break;

                    case VarType.S7WString:
                        result = (string[]?)plc.Read(DataType, db, startByteAdr, VarType.S7WString, varCount, bitAdr);
                        break;

                    case VarType.DateTime:
                        result = (DateTime[]?)plc.Read(DataType, db, startByteAdr, VarType.DateTime, varCount, bitAdr);
                        break;

                    case VarType.DateTimeLong:
                        result = (DateTime[]?)plc.Read(DataType, db, startByteAdr, VarType.DateTimeLong, varCount, bitAdr);
                        break;
                }
            }
            return result;
        }

        /// <summary>
        /// 读取示例
        /// </summary>
        /// <param name="db"></param>
        /// <param name="shifting"></param>
        public void ReadAll(int db, string shifting)
        {
            if (IsConnected())
            {
                var db1Bool1 = plc?.Read("DB1.DBX0.0");
                Console.WriteLine("DB1.DBX0.0:" + db1Bool1);

                bool? db1Bool2 = (bool?)plc?.Read("DB1.DBX0.1");
                Console.WriteLine("DB1.DBX0.1:" + db1Bool2);

                int? IntVariable = (ushort?)plc?.Read("DB1.DBW2.0");
                Console.WriteLine("DB1.DBW2.0:" + IntVariable);

                float? RealVariable = ((uint)plc?.Read("DB1.DBD4.0")).ConvertToFloat();
                Console.WriteLine("DB1.DBD4.0:" + RealVariable);

                var dIntVariable = (uint?)plc?.Read("DB1.DBD8.0");
                Console.WriteLine("DB1.DBD8.0: " + dIntVariable);

                var dWordVariable = (uint?)plc?.Read("DB1.DBD12.0");
                Console.WriteLine("DB1.DBD12.0: " + dWordVariable);

                var wordVariable = (ushort?)plc?.Read("DB1.DBW16.0");
                Console.WriteLine("DB1.DBW16.0: " + wordVariable);
            }
        }
    }
}